extends Node2D
@export var item_scece: PackedScene
# todo 使用其他生成的方式
signal end
var content:Array = [[null,null,null,null],[null,null,null,null],[null,null,null,null],[null,null,null,null]]
var col = 4;
var row = 4;
var score = 0;
var started = false;
const MOVE_UP = "up"
const MOVE_DOWN = "down"
const MOVE_LEFT = "left"
const MOVE_RIGHT = "right"
var max_value = -1
# Called when the node enters the scene tree for the first time.
#todo 使用其他生成的方式
var history:Array = [[-1,-1,-1,-1],[-1,-1,-1,-1],[-1,-1,-1,-1],[-1,-1,-1,-1]]
func _ready():
	#var a = content[4][0]
	$game_options/end.pressed.connect(self.end_game)


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	handle_input()

func start():
	init()
	started = true
	radom_generate_node_value()
	$game_over.set_visible(false)
	$game_win.set_visible(false)

func end_game():
	started = false	
	_set_visible(false)
	emit_signal("end")

func init():
	$game_options.set_visible(false)
	for i in range(0,4):
		for j in range(0,4):
			create_node(i,j,0)

func hanlde_option():
	$game_options.set_visible(not $game_options.is_visible())

func handle_input():
	if(!self.is_visible() || !self.started):
		return
	if Input.is_action_just_pressed(MOVE_RIGHT):
		move_right()
		radom_generate_node_value_with_change_check()	
	if Input.is_action_just_pressed(MOVE_LEFT): 
		move_left()
		radom_generate_node_value_with_change_check()	
	if Input.is_action_just_pressed(MOVE_DOWN):
		move_down()
		radom_generate_node_value_with_change_check()	
	if Input.is_action_just_pressed(MOVE_UP):
		move_up()
		radom_generate_node_value_with_change_check()	
	if Input.is_action_just_pressed("esc"):
		hanlde_option()

func compute_item_position(node:Node2D,array_row:int,array_col:int,px_length:int):
	var x = self.position.x
	var y = self.position.y
	var new_pos = Vector2(x+array_col*px_length,y+array_row*px_length)
	node.position = new_pos


func move_up():
	for i in range(0,col):
		handle_col_content(i,true)

func move_down():
	for i in range(0,col):
		handle_col_content(i,false)

func move_left():
	for i in range(0,row):
		handle_row_content(i,true)

func move_right():
	for i in range(0,row):
		handle_row_content(i,false)			
	

func create_node(x:int,y:int,value:int):
	var item_instance = item_scece.instantiate()
	content[x][y] = item_instance
	compute_item_position(item_instance,x,y,64)
	item_instance.set_value(value)
	$game_content.add_child(item_instance)
	
# reverse 是否反转方向
func handle_col_content(col:int,reverse:bool):
	var value_array = []
	for row in content:
		value_array.append(row[col].get_value())
	if reverse:
		value_array.reverse()
	handle(value_array,reverse)
	var i = 0
	for row in content:
		row[col].set_value(value_array[i])
		i += 1
		

func handle_row_content(row:int,reverse:bool):
	var value_array = []
	for item in content[row]:
		value_array.append(item.get_value())
	if reverse:
		value_array.reverse()	
	var i = 0
	handle(value_array,reverse)
	for item in content[row]:
		item.set_value(value_array[i])
		i += 1	
	
	
# 处理 列 / 行 的值
func handle(array:Array,reverse:bool):
	# 处理0，如果 序列是 0 x x x => x x x 0 => 2x x 0 0
	hanlde_zero(array,0)
	# 下面有2种 1， 合并 ， 2.交换
	merge_same(array,0)
	hanlde_zero(array,0)
	if reverse:
		array.reverse()
			
			
func hanlde_zero(array:Array,start:int):
	var new_array = []
	var zero_count = 0
	for i in range(start,array.size()):
		if array[i] == 0:
			zero_count += 1
		else:
			new_array.append(array[i])
	for i in range(start,zero_count+start):
		array[i] = 0		
	for i in range(start+zero_count,array.size()):
		array[i] = new_array[i-start-zero_count]

# start > 0
func merge_same(array:Array,start:int):
	for i in range(start,array.size()):
		if array[i] == array[i-1] and array[i] != 0:
			array[i] = 0;
			array[i-1] =  array[i-1] * 2;
			raise_score(array[i-1])
			hanlde_zero(array,i)
			merge_same(array,i+1)
			return	

func _set_visible(val):
	super.set_visible(val)
	if val == true:
		start()
		
func raise_score(val:int):
	if val > max_value:
		set_max_value(val)
	score += val;
	$game_score/score.text = "%d" % score;

func set_max_value(val):
	max_value = val
	if max_value > 1024:
		game_win()

func game_win():
	started = false
	$game_win.set_visible(true)
	
func game_over():
	started = false
	$game_over.set_visible(true)

func radom_generate_node_value():
	# 随机生成2 ~ 3个节点
	var target = randi_range(2,3)
	var generated = 0
	var idx = random_used_index();
	while generated < target and idx != -1:
		get_object_by_index(idx).set_value(rand_val())
		idx = random_used_index()
		generated += 1
	if generated == 0:
		game_over()
		return
	if generated > 0 and !check_could_move():
		game_over()	
			

func radom_generate_node_value_with_change_check():
	if is_changed():
		radom_generate_node_value()
		history = get_current_value_array()
  	

func rand_val():
	return 2 + (0 if self.score < 1000 else randi_range(0,1) * 2)	
		

# 随机获取一个能用的位置
func random_used_index():
	var idx = randi_range(0,self.row * self.col -1)
	# 如果无法生成则前进
	var generated = false
	var old = idx
	var max = (self.row * self.col)
	while(!generated || old != idx):
		# 位置只和row长度有关，和col无关
		if get_object_by_index(idx).get_value() == 0:
			return idx
		generated = true	
		idx = (idx + 1) % max
	return -1	

#用一个整数表示 col和 row,计算方式为  col = idx % self.row  row = idx / self.row，
func get_object_by_index(index:int):
	return self.content[index/self.row][index % self.row]
		
						
func check_could_move():
	for r in range(0,self.row):
		if(check_row_could_move(r)):
			return true
	for c in range(0,self.col):
		if check_col_could_move(c):
			return true
	return false				

func check_row_could_move(r:int):
	var pre = null
	for cur_col in range(0,self.col):
		var value = self.content[r][cur_col].get_value()
		if value == 0 || value == pre:
			return true
		pre = value
	return false		

func check_col_could_move(c:int):
	var pre = null
	for cur_row in range(0,self.row):
		var value = self.content[cur_row][c].get_value()
		if value == 0 || value == pre:
			return true
		pre = value
	return false
		
# 数组是否发生变化
func get_current_value_array():
	var cur = []
	for i in range(0,self.row):
		cur.append([])
		for j in range(0,self.col):
			cur[i].append(self.content[i][j].get_value())
	return cur		


func is_changed():
	return !is_same_array(self.history,get_current_value_array())


func is_same_array(a:Array,b:Array):
	for i in range(0,self.row):
		for j in range(0,self.col):
			if a[i][j] != b[i][j]:
				return false
	return true			

